package com.tools.web;

import com.fasterxml.jackson.databind.JsonNode;
import com.tools.common.container.*;
import com.tools.common.container.r.OnlyReadArrayList;
import com.tools.common.object.C;
import com.tools.common.object.json.JsonKit;
import com.tools.common.object.Note;
import com.tools.common.object.ObjKit;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.BufferedReader;
import java.util.*;

/**
 * Web 请求响应的常用操作类
 * 其中集合了 Servlet 工具类的操作
 * */
@Note("Web 请求响应的常用操作类。其中集合了 Servlet 工具类的操作")
public final class WebKit extends ServletKit {

    private WebKit() {}

    @Note("客户端 IP 存储在 HttpServletRequest 域里的名称")
    public static final String CLIENT_IP_NAME = "clientIP";

    @Note("公网 IP 存储在 HttpServletRequest 域中的名称")
    public static final String PUBLIC_NET_IP_NAME = "publicNetIP";

    @Note("userAgentBean 存储在 HttpServletRequest 域中的名称")
    public static final String USER_AGENT_BEAN_NAME= "userAgentBean";

    /* ********************************************************************************************
     *
     *          获取请求/响应实例等
     *
     * *********************************************************************************************
     * */

    @Note("获取 RequestAttributes 请求自定义属性容器对象")
    public static RequestAttributes getRequestAttributes() {
        return RequestContextHolder.currentRequestAttributes();
    }

    @Note("获取 ServletRequestAttributes 请求自定义属性容器对象")
    public static ServletRequestAttributes getServletRequestAttributes() {
        return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    }

    @Note("获取 HttpServletRequest 请求对象")
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
    }

    @Note("获取 HttpServletResponse 响应对象")
    public static HttpServletResponse getHttpServletResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
    }

    @Note("获取 HttpServletRequest 请求对象输入流实例")
    public static BufferedReader getRequestBufferedReader() {
        return requestBufferedReader(getHttpServletRequest());
    }

    @Note("获取 HttpServletRequest 请求对象输入流实例")
    public static ServletInputStream getRequestInputStream() {
        return requestInputStream(getHttpServletRequest());
    }

    @Note("获取 HttpServletResponse 响应对象输出流实例")
    public static ServletOutputStream getResponseOutputStream() {
        return responseOutputStream(getHttpServletResponse());
    }

    /* ********************************************************************************************
     *
     *          请求时，自定义属性 Attribute 的各种操作
     *
     * *********************************************************************************************
     * */

    public static void setAttr(String name, Object value) {
        RequestContextHolder.currentRequestAttributes()
                .setAttribute(private_asStringKey(name), value, RequestAttributes.SCOPE_REQUEST);
    }

    public static void setAttrs(Map attrs) {
        if(attrs == null || attrs.isEmpty()) return;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for(Object o : attrs.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            attributes.setAttribute(private_asStringKey(e.getKey()), e.getValue(), RequestAttributes.SCOPE_REQUEST);
        }
    }

    public static void setAttrs(OwnPairs attrs) {
        if(attrs != null && !attrs.isEmpty()) {
            RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
            for(Object e : attrs.entrySet()) {
                Map.Entry entry = (Map.Entry) e;
                attributes.setAttribute(private_asStringKey(entry.getKey()), entry.getValue(), RequestAttributes.SCOPE_REQUEST);
            }
        }
    }

    public static void setAttrs(Object beanEntity) {
        if(beanEntity == null) return;
        Map<String, Object> valuesMap = ObjKit.allFieldValue(beanEntity);
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (Map.Entry<String, Object> e : valuesMap.entrySet()) {
            attributes.setAttribute(e.getKey(), e.getValue(), RequestAttributes.SCOPE_REQUEST);
        }
    }

    public static void setAttrs(String jsonObject) {
        if(private_isJsonObject(jsonObject)) {
            JsonNode jsonNode = JsonKit.readToNode(jsonObject);
            Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
            RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> e = fields.next();
                JsonNode node = e.getValue();
                if(node != null) {
                    Object value = ObjKit.stringToRealValue(node.toString(), true);
                    attributes.setAttribute(e.getKey(), value, RequestAttributes.SCOPE_REQUEST);
                    continue;
                }
                attributes.setAttribute(e.getKey(), "", RequestAttributes.SCOPE_REQUEST);
            }
        }
    }

    public static Object getAttr(String name) {
        return RequestContextHolder.currentRequestAttributes()
                .getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST);
    }

    public static <T> T getAttr(String name, Class<T> valueType) {
        return (T) RequestContextHolder.currentRequestAttributes()
                .getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST);
    }

    public static <T> T getAttr(String name, C<T> valueType) {
        Object value = RequestContextHolder.currentRequestAttributes()
                .getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST);
        return (T) ObjKit.toRealValue(value, valueType);
    }

    public static Map<String, Object> getAttrsAsMap() {
        return getAttrsAsMap(MapEnum.HASH);
    }

    public static Map<String, Object> getAttrsAsMap(MapEnum mapEnum) {
        if(mapEnum == null) throw new NullPointerException("构建目标 Map 的实现类枚举不能为 null");
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        String[] names = attributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST);
        Map<String, Object> result = mapEnum.newMap();
        for (String name : names) {
            result.put(name, attributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST));
        }
        return result;
    }

    public static OwnMap<String, Object> getAttrsAsOwnMap() {
        return getAttrsAsOwnMap(MapEnum.HASH);
    }

    public static OwnMap<String, Object> getAttrsAsOwnMap(MapEnum mapEnum) {
        if(mapEnum == null) throw new NullPointerException("构建目标 Map 的实现类枚举不能为 null");
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        String[] names = attributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST);
        return (OwnMap<String, Object>) private_ownPairsPuts(new OwnMap<>(mapEnum, names.length), attributes, names);
    }

    public static OwnPair getAttrsAsOwnPair() {
        return getAttrsAsOwnPair(MapEnum.HASH);
    }

    public static OwnPair getAttrsAsOwnPair(MapEnum mapEnum) {
        if(mapEnum == null) throw new NullPointerException("构建目标 Map 的实现类枚举不能为 null");
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        String[] names = attributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST);
        return (OwnPair) private_ownPairsPuts(new OwnPair(mapEnum, names.length), attributes, names);
    }

    public static JsonPair getAttrsAsJsonPair() {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        String[] names = attributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST);
        JsonPair json = new JsonPair(-1, "");
        for (String name : names) {
            json.put(name, attributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST));
        }
        return json;
    }

    public static <T> T getAttrsAsBean(C<T> beanType) {
        Map<String, Object> map = getAttrsAsMap();
        return ObjKit.buildObject(beanType, map);
    }

    public static void removeAttr(String name) {
        RequestContextHolder.currentRequestAttributes().removeAttribute(name, RequestAttributes.SCOPE_REQUEST);
    }

    public static void removeAttrs(String... names) {
        if(names == null || names.length == 0) return;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for(String name : names) attributes.removeAttribute((name == null) ? "null" : name, RequestAttributes.SCOPE_REQUEST);
    }

    public static void removeAttrs(Iterable<String> names) {
        if(names == null) return;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for(String name : names) attributes.removeAttribute((name == null) ? "null" : name, RequestAttributes.SCOPE_REQUEST);
    }

    public static void clearAttrs() {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (String name : attributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST)) {
            attributes.removeAttribute(name, RequestAttributes.SCOPE_REQUEST);
        }
    }

    public static String[] getAttrNames() {
        return RequestContextHolder.currentRequestAttributes().getAttributeNames(RequestAttributes.SCOPE_REQUEST);
    }

    public static boolean contains(String attributeName) {
        return RequestContextHolder.currentRequestAttributes()
                .getAttribute(private_asStringKey(attributeName), RequestAttributes.SCOPE_REQUEST) != null;
    }

    public static boolean containsAll(String... attributeNames) {
        if(attributeNames == null || attributeNames.length == 0) return false;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (String name : attributeNames) {
            if(attributes.getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST) == null) return false;
        }
        return true;
    }

    public static boolean containsAll(Iterable<String> attributeNames) {
        if(attributeNames == null) return false;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (String name : attributeNames) {
            if(attributes.getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST) == null) return false;
        }
        return true;
    }

    public static boolean containsAny(String... attributeNames) {
        if(attributeNames == null || attributeNames.length == 0) return false;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (String name : attributeNames) {
            if(attributes.getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST) != null) return true;
        }
        return false;
    }

    public static boolean containsAny(Iterable<String> attributeNames) {
        if(attributeNames == null) return false;
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        for (String name : attributeNames) {
            if(attributes.getAttribute(private_asStringKey(name), RequestAttributes.SCOPE_REQUEST) != null) return true;
        }
        return false;
    }

    /* ********************************************************************************************
     *
     *          请求时，请求参数的各种操作
     *
     * *********************************************************************************************
     * */

    public static Object getParam(String name) {
        String parameter = getHttpServletRequest().getParameter(name);
        return ObjKit.stringToRealValue(parameter, true);
    }

    public static <T> T getParam(String name, C<T> paramType) {
        String parameter = getHttpServletRequest().getParameter(name);
        return (T) ObjKit.stringToRealValue(parameter, paramType);
    }

    public static Map<String, Object> getParamMap() {
        return getParamMap(MapEnum.HASH);
    }

    public static Map<String, Object> getParamMap(MapEnum mapEnum) {
        HttpServletRequest request = getHttpServletRequest();
        Map<String, String[]> map = request.getParameterMap();
        if(map == null || map.isEmpty()) return new HashMap<>();
        Map<String, Object> result = mapEnum.newMap(map.size());
        for(Map.Entry<String, String[]> e : map.entrySet()) {
            String key = e.getKey();
            String[] array = e.getValue();
            result.put(key, private_paramStringArrayToRealValue(array));
        }
        return result;
    }

    public static OwnMap<String, Object> getParamOwnMap() {
        return getParamOwnMap(MapEnum.HASH);
    }

    public static OwnMap<String, Object> getParamOwnMap(MapEnum mapEnum) {
        HttpServletRequest request = getHttpServletRequest();
        Map<String, String[]> map = request.getParameterMap();
        if(map == null || map.isEmpty()) return new OwnMap<>(mapEnum);
        OwnMap<String, Object> result = new OwnMap<>(mapEnum, map.size());
        for(Map.Entry<String, String[]> e : map.entrySet()) {
            String key = e.getKey();
            String[] array = e.getValue();
            result.put(key, private_paramStringArrayToRealValue(array));
        }
        return result;
    }

    public static OwnPair getParamOwnPair() {
        return getParamOwnPair(MapEnum.HASH);
    }

    public static OwnPair getParamOwnPair(MapEnum mapEnum) {
        HttpServletRequest request = getHttpServletRequest();
        Map<String, String[]> map = request.getParameterMap();
        if(map == null || map.isEmpty()) return new OwnPair(mapEnum);
        OwnPair result = new OwnPair(mapEnum, map.size());
        for(Map.Entry<String, String[]> e : map.entrySet()) {
            String key = e.getKey();
            String[] array = e.getValue();
            result.put(key, private_paramStringArrayToRealValue(array));
        }
        return result;
    }


    public static JsonPair getParamJsonPair() {
        HttpServletRequest request = getHttpServletRequest();
        Map<String, String[]> map = request.getParameterMap();
        if(map == null || map.isEmpty()) return new JsonPair(-1, "");
        JsonPair result = new JsonPair(-1, "");
        for(Map.Entry<String, String[]> e : map.entrySet()) {
            String key = e.getKey();
            String[] array = e.getValue();
            result.put(key, private_paramStringArrayToRealValue(array));
        }
        return result;
    }


    public static <T> T getParamBean(C<T> beanType) {
        return ObjKit.buildObject(beanType, getHttpServletRequest());
    }

    /* ********************************************************************************************
     *
     *          请求时，请求头的各种操作
     *
     * *********************************************************************************************
     * */

    public static String getHeader(String s) {
        return getHttpServletRequest().getHeader(private_asStringKey(s));
    }

    public static Enumeration<String> getHeaders() {
        return getHttpServletRequest().getHeaderNames();
    }

    public static Map<String, String> getHeaderMap() {
        return getHeaderMap(MapEnum.HASH);
    }

    public static Map<String, String> getHeaderMap(MapEnum mapEnum) {
        if(mapEnum == null) throw new NullPointerException("构建目标 Map 的实现类枚举不能为 null");
        HttpServletRequest request = getHttpServletRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> result = mapEnum.newMap();
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            result.put(s, request.getHeader(s));
        }
        return result;
    }

    public static OwnMap<String, String> getHeaderOwnMap() {
        return getHeaderOwnMap(MapEnum.HASH);
    }

    public static OwnMap<String, String> getHeaderOwnMap(MapEnum mapEnum) {
        if(mapEnum == null) throw new NullPointerException("构建目标 Map 的实现类枚举不能为 null");
        HttpServletRequest request = getHttpServletRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        OwnMap<String, String> result = new OwnMap<>(mapEnum);
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            result.put(s, request.getHeader(s));
        }
        return result;
    }

    public static Properties getHeaderProperties() {
        HttpServletRequest request = getHttpServletRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        Properties properties = new Properties();
        while (headerNames.hasMoreElements()) {
            String s = headerNames.nextElement();
            properties.setProperty(s, request.getHeader(s));
        }
        return properties;
    }

    public static String getMethod() {
        return getHttpServletRequest().getMethod();
    }

    public static String getContentType() {
        return getHttpServletRequest().getContentType();
    }

    public static String getCharacterEncoding() {
        return getHttpServletRequest().getCharacterEncoding();
    }

    /* ********************************************************************************************
     *
     *          其他方法
     *
     * *********************************************************************************************
     * */

    public static String getClientIP() {
        HttpServletRequest request = getHttpServletRequest();
        String clientIP = (String) request.getAttribute(CLIENT_IP_NAME);
        if(clientIP != null) {
            return clientIP;
        }
        clientIP = clientIP(request);
        request.setAttribute(CLIENT_IP_NAME, clientIP);
        return clientIP;
    }

    public static String getPublicNetIP() {
        HttpServletRequest request = getHttpServletRequest();
        String publicNetIP = (String) request.getAttribute(PUBLIC_NET_IP_NAME);
        if(publicNetIP == null) {
            publicNetIP = clientIP(request);
            request.setAttribute(PUBLIC_NET_IP_NAME, publicNetIP);
        }
        return publicNetIP;
    }

    public static OnlyReadArrayList<String> getAllProxyIP() {
        HttpServletRequest request = getHttpServletRequest();
        OnlyReadArrayList<String> allProxyIP = (OnlyReadArrayList<String>) request.getAttribute("proxyAllRemote");
        if(allProxyIP == null) {
            allProxyIP = allProxyIP(request);
            request.setAttribute("allProxyIP", allProxyIP);
        }
        return allProxyIP;
    }


    public static String getReferer() {
        HttpServletRequest request = getHttpServletRequest();
        String referer = (String) request.getAttribute("referer");
        if(referer == null) {
            referer = referer(request);
            request.setAttribute("referer", referer);
        }
        return referer;
    }

    public static String geServerRootPath(){
        return getHttpServletRequest().getServletContext().getRealPath("/");
    }

    public static String getRequestURI() {
        return getHttpServletRequest().getRequestURI();
    }

    public static String getRequestURL() {
        return getHttpServletRequest().getRequestURL().toString();
    }

    public static String getServletPath() {
        return getHttpServletRequest().getServletPath();
    }

    public static String getServerName() {
        return getHttpServletRequest().getServerName();
    }

    public static String getContextPath() {
        return getHttpServletRequest().getContextPath();
    }

    public static String getRemoteAddr() {
        return getHttpServletRequest().getRemoteAddr();
    }

    public static String getRemoteHost() {
        return getHttpServletRequest().getRemoteHost();
    }

    public static int getRemotePort() {
        return getHttpServletRequest().getRemotePort();
    }

    public static String getRemoteUser() {
        return getHttpServletRequest().getRemoteUser();
    }

    public static UserAgentBean getUserAgentBean() {
        HttpServletRequest request = getHttpServletRequest();
        UserAgentBean userAgentBean = (UserAgentBean) request.getAttribute(USER_AGENT_BEAN_NAME);
        if(userAgentBean != null) {
            return userAgentBean;
        }
        userAgentBean = new UserAgentBean(userAgent(request));
        request.setAttribute(USER_AGENT_BEAN_NAME, userAgentBean);
        return userAgentBean;
    }

    public static String uriAddServerContextPath(String uri) {
        HttpServletRequest request = getHttpServletRequest();
        String contextPath = request.getServletContext().getContextPath();
        return uriAddServerContextPath(uri, contextPath);
    }

    public static String uriAddServerContextPath(String uri, String contextPath) {
        if(uri != null && !uri.isEmpty()) {
            if(uri.startsWith(contextPath)) return uri;
            if(uri.startsWith("/")) return contextPath + uri;
            return contextPath + '/' + uri;
        }
        return "";
    }

    /* ********************************************************************************************
     *
     *          私有逻辑
     *
     * *********************************************************************************************
     * */

    @Note("将 null 的 key 转为字符串 null")
    private static String private_asStringKey(Object key) {
        return (key == null) ? "null" : key.toString();
    }

    @Note("简易地判断字符串是否是 json 对象格式")
    private static boolean private_isJsonObject(String s) {
        return s != null && s.startsWith("{") && s.endsWith("}");
    }

    @Note("将 RequestAttribute 域里的 key-value 数据转到自定义容器 result 里")
    private static OwnPairs<String, Object> private_ownPairsPuts(OwnPairs<String, Object> result, RequestAttributes attributes, String[] names) {
        for (String name : names) {
            result.put(name, attributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST));
        }
        return result;
    }

    @Note("将请求参数的数组转为相对真实的类型返回")
    private static Object private_paramStringArrayToRealValue(String[] array) {
        if(array == null || array.length == 0) return null;
        if(array.length > 1) {
            ArrayList<Object> list = new ArrayList<>(array.length);
            for(String value : array) {
                Object realValue = ObjKit.stringToRealValue(value, true);
                list.add(realValue);
            }
            return list;
        }
        return ObjKit.stringToRealValue(array[0], true);
    }
}
